#include "pix.hpp"
#include <QDebug>

Pix::Pix()
{
    decoder = new Decoder(this);
    programManagement = new ProgramManagement(this);
    dataMemoryOrganisation = new DataMemoryOrganisation(this);
    stackManagement = new StackManagement(this);
    watchdog = new Watchdog(dataMemoryOrganisation, this);
    cpu = new CPU(dataMemoryOrganisation, stackManagement, watchdog, this);
    programCounter = 0;
    this->waitInterval = 60;
    this->fastMode = false;
}

Pix::~Pix()
{
    //delete decoder;
    delete cpu;
    delete programManagement;
    delete stackManagement;
    delete watchdog;
}

void Pix::readCodeFile(QString filepath)
{
    //Open file and decode the file line by line
    QFile file(filepath);
    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
	while (!file.atEnd()) {
	    programManagement->addCommand(decoder->decodeLstString(file.readLine()));
	}
	file.close();
    }
}

ProgramManagement *Pix::getProgramManagement()
{
    return programManagement;
}

DataMemoryOrganisation *Pix::getDataMemoryOrganisation()
{
    return dataMemoryOrganisation;
}

Decoder *Pix::getDecoder() const
{
    return decoder;
}

StackManagement *Pix::getStackManagement()
{
    return stackManagement;
}

Watchdog *Pix::getWatchdog()
{
    return watchdog;
}

CPU *Pix::getCPU()
{
    return cpu;
}

void Pix::setSimulationMode(simulationModeEnum simulationMode)
{
    this->simulationMode = simulationMode;
}


void Pix::reset()
{
    //Reset Pix
    this->decoder->reset();
    this->programManagement->reset();
    this->stackManagement->reset();
    this->watchdog->clearWatchdogTimer();
    this->dataMemoryOrganisation->reset();
    this->simulationMode = Pix::STOP;
    this->programCounter = 0;
    //emit programCounterChanged(programCounter);
}


void Pix::resetToProgramStart()
{
    this->stackManagement->reset();
    this->watchdog->clearWatchdogTimer();
    this->cpu->reset();
    this->dataMemoryOrganisation->reset();
    this->simulationMode = Pix::STOP;
    this->programCounter = 0;
    //emit programCounterChanged(programCounter);
}


void Pix::stop()
{
    this->simulationMode = STOP;
    this->setTerminationEnabled(true);
    this->resetToProgramStart();
}


void Pix::pause()
{
    this->simulationMode = PAUSE;
}


void Pix::run()
{
    emit simulationStateChanged(true, this->programCounter);
    this->setTerminationEnabled(false);
    int i = 0;

    while (programManagement->getCommandMap()->contains(programCounter))
    {
	if(this->simulationMode == Pix::SLEEP)
	{
	    watchdog->incrementWatchdogTimer();
	    usleep(1);

	} else {
	    programCounter = cpu->executeCommand(programManagement->getCommandMap()->find(programCounter).value(), programCounter);

	    emit programCounterChanged(programCounter);

	    // Check for Breakpoint
	    if (this->programManagement->hasBreakpointOnLine(programCounter))
		this->simulationMode = Pix::BREAK;

	    // Check if Simulation should be stopped
	    if (simulationMode != Pix::RUN)
		break;

	    if (!this->fastMode)
		msleep(this->waitInterval);
	    else
		usleep(0);

	    if ((++i % 25) == 0)
		QThread::yieldCurrentThread();
	}
    }

    if(simulationMode == Pix::STOP)
    {
	this->stop();
    }

    emit programCounterChanged(programCounter);
    emit simulationStateChanged(false, this->programCounter);
}


void Pix::setFastMode(bool state)
{
    if (state)
    {
	this->waitInterval = 1;
	this->fastMode = true;
    } else {
	this->waitInterval = 25;
	this->fastMode = false;
    }
}


int Pix::getProgramCounter() const
{
    return this->programCounter;
}
